/*file chiron src/color.js */
/*preamble

    Copyright (c) 2002-2008 Kris Kowal <http://cixar.com/~kris.kowal>
    MIT License
    
    The license terms are stated in full in <license.rst> and at the end
    of all source files.

*/


/**
    provides colors and composition functions supporting HSL, HSB, and RGB.
*/
/*status requires testing */

var base = include('base');
include('browser');

/*** rgb */
/*** rgba */
/*** hsl */
/*** hsla */
/*** hsb */
/*** hsba */

/*** parse
    returns a ``Color`` object corresponding to a color
    string, albeit a color name (see ``names``),
    a three, four, six, or seven  character hexadecimal string
    with or without the hash (``#``) prefix.

*/
exports.parse = function (components) {
    components = parseComponents(components);
    return Color(components, 256);
};

/*** parseComopnents
    returns a list containing the corresponding RGB
    components in the range [0, 256) to a color
    string, albeit a color name (see ``names``),
    a three, four, six, or seven  character hexadecimal string
    with or without the hash (``#``) prefix, or
    a CSS RGB string like ``rgb(255, 255, 255)``.
*/
var parseComponentsExpression = /^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/;
exports.parseComponents = function (components) {

    /* css rgb string */
    if (parseComponentsExpression.test(components)) {
        return arrayEach(
            parseComponentsExpression.exec(components).slice(1),
            parseInt
        );
    }

    /* color names */
    if (has(names, components)) {
        components = names[components];
    }

    /* optional hash prefix */
    if (components[0] == '#') {
        components = components.substring(1);
    }

    /* hex strings */
    if (components.length == 3 || components.length == 4) {
        return arrayEach([
            components[0],
            components[1],
            components[2],
        ], function (component) {
            /*
            colors, being the fickle [inclusive continua - 1]
            that they are, seem to require quite a bit 
            of normalization.
            */
            return parseInt(component, 16) / 15 * 255;
        });
    } else if (components.length == 6) {
        return arrayEach([
            components.substr(0, 2),
            components.substr(2, 2),
            components.substr(4, 2)
        ], function (component) {
            return parseInt(component, 16);
        });
    } else if (components.length == 8) {
        return arrayEach([
            components.substr(0, 2),
            components.substr(2, 2),
            components.substr(4, 2),
            components.substr(6, 2)
        ], function (component) {
            return parseInt(component, 16);
        });
    } else {
        throw new TypeError("Not a color string.");
    }

    return components;
};

var hexChars = "0123456789abcdef";

/*** formatHex
    returns a two-character lower-case hexadecimal
    string.
    accepts an integer in the interval
    ``[0, 256)``.
*/
exports.formatHex = function (number) {
    var high = Math.floor(number / 16);
    var low = Math.floor(number) % 16;
    if (high >= 16 || low >= 16) {
        throw new Error(number + ' is not in the color spectrum.');
    }
	return hexChars[high] + hexChars[low];
}

/*** format
    returns a 6 or 8 character hexadecimal string in
    lower-case, preceded by the hash (``#``) character.
    accepts an array of integer color components,
    each in the interval ``[0, 256)``.
*/
exports.format = function (components) {
	return '#' + arrayEach(components, formatHex).join('');
};

/*** mix
    mixes two colors.
    accepts an optional ratio in the interval ``[0, 1]``
    where 0 returns the left color and 1
    returns the right color.
    By default, the ratio is ``.5``
*/
exports.mix = function (left, right, ratio) {

    if (no(ratio))
        ratio = .5;

    left = Color(left);
    right = Color(right);

    left.setModeRgb();
    right.setModeRgb();

    left.setRadix();
    right.setRadix();

    var leftComponents = left.getComponents();
    var rightComponents = right.getComponents();

    var components = [];

    for (var index = 0; index < 3; index++) {
		components[index] = (
			leftComponents[index] * (1.0 - ratio) + 
			rightComponents[index] * ratio
		);
	}
    
    return Color(components, 1, rgbMode, inclusive);
};

/*** transition
    returns a transition function that gradually transitions from
    one color to another based on a percentage, a value in the
    range [0, 1].
*/
exports.transition = function (left, right) {
    left = Color(left);
    right = Color(right);
    left.setModeRgb();
    right.setModeRgb();
    return function (ratio) {
        return mix(left, right, ratio);
    };
};

/*** rgbMode
    a constant that indicates the Red, Green, and Blue
    color space.
*/
exports.rgbMode = 'rgb';

/*** hslMode
    a constant that indicates the Hue, Saturation, and Lightness
    color space.
*/
exports.hslMode = 'hsl';

/*** inclusive
    a constant that indicates that a color's component values are in
    the interval ``[0, radix]``, or zero to the radix, inclusive. 
    ``inclusive`` color component ranges are appropriate for non-integer
    bounds, like raidx 1 with [0, 1].
*/
exports.inclusive = true;

/*** exclusive
    a constant that indicates that a color values are in the interval
    ``[0, radix)``, or zero to the maximum value exclusive.
    Specifically, with integer color values, these are values from
    zero to one less than the radix, inclusive.
    ``exclusive`` color ranges are appropriate for bit-field colors
    like base 256 with a range of ``[0, 255]`` or ``[0, 255)``
    for integers only.
*/
exports.exclusive = false;

/*** Color
    a representation of a color that provides accessors and mutators
    for its components in the HSL, RGB, and HSB color spaces under
    any radix and cardinality including an alpha channel.

    Usage::

	    Color(color)
	    Color(
            components, [radix, [rgbMode | hslMode],
            [inclusive | exclusive]]]
        )
	
	    # red
	    Color([1, 0, 0])
	    Color([255, 0, 0], 256)
	    Color([0, 1, 1], 1, hslMode)
*/
exports.Color = type(function (self, supr, alias, ed) {

    var components;
    var radix;
    var mode;
    var inclusive;

    self.init = function (_components, _radix, _mode, _inclusive) {
        components = _components;
        radix = _radix;
        mode = _mode;
        inclusive = _inclusive;

        if (!radix) {
            radix = 1;
        }
        if (no(inclusive)) {
            inclusive = radix == 1 || radix == 360;
        }

        if (no(components)) {
            components = [0, 0, 0, radix == 1? 1 : radix - 1];
        } else if (isInstance(components, Color)) {
            radix = components.getRadix();
            mode = components.getMode();
            inclusive = components.getOrdinal();
            /* must be last since it overwrites the components var: */
            components = components.getComponents();
        } else if (isInstance(components, String)) {
            radix = 256;
            inclusive = false;
            components = parseComponents(components);
            if (components.length == 3) {
                components[3] = 255;
            }
        } else if (components.iter) {
            components = array(components);
        }

        if (no(mode)) {
            mode = rgbMode;
        }

        supr.init(arraySliced(arguments, 4));
    };

    /**** getRed */
    self.getRed = function (radix, inclusive) {
        self.setModeRgb();
        self.setRadix(radix, inclusive);
        return components[0];
    };
    /**** setRed */
    self.setRed = function (value, radix, inclusive) {
        self.setModeRgb();
        self.setRadix(radix, inclusive);
        components[0] = value;
    };

    /**** getGreen */
    self.getGreen = function (radix, inclusive) {
        self.setModeRgb();
        self.setRadix(radix, inclusive);
        return components[1];
    };
    /**** setGreen */
    self.setGreen = function (value, radix, inclusive) {
        self.setModeRgb();
        self.setRadix(radix, inclusive);
        components[1] = value;
    };

    /**** getBlue */
    self.getBlue = function (radix, inclusive) {
        self.setModeRgb();
        self.setRadix(radix, inclusive);
        return components[2];
    };
    /**** setBlue */
    self.setBlue = function (value, radix, inclusive) {
        self.setModeRgb();
        self.setRadix(radix, inclusive);
        components[2] = value;
    };

    /**** getHue */
    self.getHue = function (radix, inclusive) {
        self.setModeHsl();
        self.setRadix(radix, inclusive);
        return components[0];
    };
    /**** setHue */
    self.setHue = function (value, radix, inclusive) {
        self.setModeHsl();
        self.setRadix(radix, inclusive);
        components[0] = value % radix;
    };

    /**** getSaturation */
    self.getSaturation = function (radix, inclusive) {
        self.setModeHsl();
        self.setRadix(radix, inclusive);
        return components[1];
    };
    /**** setSaturation */
    self.setSaturation = function (value, radix, inclusive) {
        self.setModeHsl();
        self.setRadix(radix, inclusive);
        components[1] = value;
    };

    /**** getLightness */
    self.getLightness = function (radix, inclusive) {
        self.setModeHsl();
        self.setRadix(radix, inclusive);
        return components[2];
    };
    /**** setLightness */
    self.setLightness = function (value, radix, inclusive) {
        self.setModeHsl();
        self.setRadix(radix, inclusive);
        components[2] = value;
    };

    /* brightness formula [5] */
    /**** getBrightness */
    self.getBrightness = function (radix, inclusive) {
        self.setModeRgb();
        self.setRadix(radix, inclusive);
        var red = components[0];
        var green = components[1];
        var blue = components[2];
        return red * .299 + green * .587 + blue * .114;
    };

    /**** getOpacity */
    self.getOpacity = function (radix, inclusive) {
        self.setRadix(radix, inclusive);
        return components[3];
    };
    /**** setOpacity */
    self.setOpacity = function (value, radix, inclusive) {
        self.setRadix(radix, inclusive);
        components[3] = value;
    }

    /**** mix */
    self.mix = function (other, ratio) {
        var replacement = mix(self, other, ratio);
        mode = replacement.getMode();
        radix = replacement.getRadix();
        inclusive = replacement.getOrdinal();
        components = replacement.getComponents();
    };

    /**** mixed */
    self.mixed = function (other, ratio) {
        return mix(self, other, ratio);
    };

    /**** contrast */
    self.contrast = function () {
        var brightness = self.getBrightness();
        self.setModeHsl();
        self.setRadix(1);
        components = [
            0,
            0,
            brightness > .5 ? brightness / 2 : 1 - brightness / 2,
            components[3]
        ];
        return self;
    };

    /**** contrasted */
    self.contrasted = ed('contrast');

    /**** invert */
    self.invert = function () {
        self.setModeHsl();
        self.setRadix(1);
        components = [
            1 - components[0],
            1 - components[1],
            1 - components[2],
            components[3]
        ];
    };

    /**** inverted */
    self.inverted = ed('invert');

    /* [6] */
    /**** diff */
    self.diff = function (other) {
        self.setModeRgb();
        self.setRadix(1, inclusive);
        other = Color(other);
        other.setModeRgb();
        other.setRadix(1, inclusive);
        otherComponents = other.getComponents();
        return sum(each(range(3), function (n) {
            return (
                max(components[n], otherComponents[n]) -
                min(components[n], otherComponents[n])
            );
        })) / 3;
    };

    /**** copy */
    self.copy = function () {
        return Color(array(components), radix, mode, inclusive);
    };

    /**** hsl */
    self.hsl = function (radix, inclusive) {
        var dup = self.copy();
        dup.setModeHsl();
        dup.setRadix(radix, inclusive);
        return dup.getComponents().slice(0, 3);
    };

    /**** rgb */
    self.rgb = function (radix, inclusive) {
        var dup = self.copy();
        dup.setModeRgb();
        dup.setRadix(radix, inclusive);
        return dup.getComponents().slice(0, 3);
    };

    /**** hslString */
    self.hslString = function () {
        var dup = self.copy();
        dup.setModeHsl();
        dup.setRadix(256);
        return format(dup.getComponents().slice(0, 3));
    };

    /**** rgbString */
    self.rgbString = function () {
        var dup = self.copy();
        dup.setModeRgb();
        dup.setRadix(256);
        return format(dup.getComponents().slice(0, 3));
    };

    /**** string */
    self.string = alias('rgbString');

    /**** repr */
    self.repr = function () {
        return self.getTypeName() + '("' + self.string() + '")';
    };

    /**** getComponents */
    self.getComponents = function () {
        return array(components);
    };
    /**** setComponents */
    self.setComponents = function (value, radix, inclusive) {
        self.setRadix(radix, inclusive);
        components = array(value);
    };

    /**** getState */
    self.getState = function () {
        return [
            components,
            mode,
            radix,
            (inclusive ? 'inclusive' : 'exclusive')
        ];
    };

    /**** setRadix */
    self.setRadix = function (_radix, _inclusive) {

        if (no(_radix)) {
            _radix = 1;
        }
        if (no(_inclusive)) {
            _inclusive = _radix == 1 || _radix == 360;
        }

        var max = inclusive ? radix : radix - 1;
        components[0] /= max;
        components[1] /= max;
        components[2] /= max;
        components[3] /= max;

        radix = _radix;
        inclusive = _inclusive;

        var max = inclusive ? radix : radix - 1;
        components[0] *= max;
        components[1] *= max;
        components[2] *= max;
        components[3] *= max;

    };

    /**** getRadix */
    self.getRadix = function () {
        return radix;
    };

    /**** getOrdinal */
    self.getOrdinal = function () {
        return inclusive;
    };

    /**** getMode */
    self.getMode = function () {
        return mode;
    };

    /**** setMode */
    self.setMode = function (value) {
        if (value == rgbMode) {
            self.setModeRgb();
        } else if (value == hslMode) {
            self.setModeHsl();
        } else {
            throw new TypeError("Mode not supported.");
        }
    }

    /**** setModeRgb */
    self.setModeRgb = function () {
        if (mode != rgbMode) {
            self.setRadix();

            var hue = components[0];
            var saturation = components[1];
            var brightness = components[2];

            var red = brightness;
            var green = brightness;
            var blue = brightness;

            if (saturation) {

                var ratio = hue * 6 % 1;
                var high = brightness * (1 - saturation);
                var left = brightness * (1 - saturation * ratio)
                var right = brightness * (1 - saturation * (1 - ratio))
                var section = Math.floor(hue * 6);

                if (section == 0) { /* red to yellow */
                    red = brightness;
                    green = right;
                    blue = high;
                } else if (section == 1) { /* yellow to green */
                    red = left;
                    green = brightness;
                    blue = high;
                } else if (section == 2) { /* green to cyan */
                    red = high;
                    green = brightness;
                    blue = right;
                } else if (section == 3) { /* cyan to blue */
                    red = high;
                    green = left;
                    blue = brightness;
                } else if (section == 4) { /* blue to magenta */
                    red = right;
                    green = high;
                    blue = brightness;
                } else { /* magenta to red */
                    red = brightness;
                    green = high;
                    blue = left;
                }

            }

            components[0] = red;
            components[1] = green;
            components[2] = blue;
            mode = rgbMode;
        };
    };

    /**** setModeHsl */
    self.setModeHsl = function () {
        if (mode != hslMode) {
            self.setRadix();

            var red = components[0];
            var green = components[1];
            var blue = components[2];

            var min = base.min(red, green, blue);
            var max = base.max(red, green, blue);
            var delta = max - min;
            var brightness = max;
            var saturation = max == 0 ? 0 : delta / max;
            var hue = 0;
            if (saturation) {
                var redRatio = (max - red) / delta;
                var greenRatio = (max - green) / delta;
                var blueRatio = (max - blue) / delta;
                if (red == max) {
                    hue = blueRatio - greenRatio;
                } else if (green == max) {
                    hue = 2 + redRatio - blueRatio ;
                } else {
                    hue = 4 + greenRatio - redRatio;
                }
                if (hue < 0) {
                    hue++;
                }
            }

            components[0] = hue / 6;
            components[1] = saturation;
            components[2] = brightness;
            mode = hslMode;
        };
    };

    /**** eq */
    self.eq = function (other) {
        other = Color(other);
        other.setRadix(radix);
        other.setMode(mode);
        return eq(components, other.getComponents());
    };

    /**** hash */
    self.hash = alias('string');

});

/* based on MochiKit/Color.js */

/*** names
    an ``Object`` containing RGB hexadecimal strings for color names in
    camelCase.  All values in ``names`` are also in the module
    name space.
*/
exports.names = {
    /**** aliceBlue */ aliceBlue: "#f0f8ff",
    /**** antiqueWhite */ antiqueWhite: "#faebd7",
    /**** aqua */ aqua: "#00ffff",
    /**** aquaMarine */ aquaMarine: "#7fffd4",
    /**** azure */ azure: "#f0ffff",
    /**** beige */ beige: "#f5f5dc",
    /**** bisque */ bisque: "#ffe4c4",
    /**** black */ black: "#000000",
    /**** blanchedAlmond */ blanchedAlmond: "#ffebcd",
    /**** blue */ blue: "#0000ff",
    /**** blueViolet */ blueViolet: "#8a2be2",
    /**** brown */ brown: "#a52a2a",
    /**** burlyWood */ burlyWood: "#deb887",
    /**** cadetBlue */ cadetBlue: "#5f9ea0",
    /**** chartreuse */ chartreuse: "#7fff00",
    /**** chocolate */ chocolate: "#d2691e",
    /**** coral */ coral: "#ff7f50",
    /**** cornFlowerBlue */ cornFlowerBlue: "#6495ed",
    /**** cornsilk */ cornsilk: "#fff8dc",
    /**** crimson */ crimson: "#dc143c",
    /**** cyan */ cyan: "#00ffff",
    /**** darkBlue */ darkBlue: "#00008b",
    /**** darkCyan */ darkCyan: "#008b8b",
    /**** darkGoldenrod */ darkGoldenrod: "#b8860b",
    /**** darkGray */ darkGray: "#a9a9a9",
    /**** darkGreen */ darkGreen: "#006400",
    /**** darkGrey */ darkGrey: "#a9a9a9",
    /**** darkKhaki */ darkKhaki: "#bdb76b",
    /**** darkMagenta */ darkMagenta: "#8b008b",
    /**** darkOlivegreen */ darkOlivegreen: "#556b2f",
    /**** darkOrange */ darkOrange: "#ff8c00",
    /**** darkOrchid */ darkOrchid: "#9932cc",
    /**** darkRed */ darkRed: "#8b0000",
    /**** darkSalmon */ darkSalmon: "#e9967a",
    /**** darkSeaGreen */ darkSeaGreen: "#8fbc8f",
    /**** darkSlateBlue */ darkSlateBlue: "#483d8b",
    /**** darkSlateGray */ darkSlateGray: "#2f4f4f",
    /**** darkSlateGrey */ darkSlateGrey: "#2f4f4f",
    /**** darkTurquoise */ darkTurquoise: "#00ced1",
    /**** darkViolet */ darkViolet: "#9400d3",
    /**** deepPink */ deepPink: "#ff1493",
    /**** deepSkyBlue */ deepSkyBlue: "#00bfff",
    /**** dimGray */ dimGray: "#696969",
    /**** dimGrey */ dimGrey: "#696969",
    /**** dodgerBlue */ dodgerBlue: "#1e90ff",
    /**** fireBrick */ fireBrick: "#b22222",
    /**** floralWhite */ floralWhite: "#fffaf0",
    /**** forestGreen */ forestGreen: "#228b22",
    /**** fuchsia */ fuchsia: "#ff00ff",
    /**** gainsboro */ gainsboro: "#dcdcdc",
    /**** ghostWhite */ ghostWhite: "#f8f8ff",
    /**** gold */ gold: "#ffd700",
    /**** goldenrod */ goldenrod: "#daa520",
    /**** gray */ gray: "#808080",
    /**** green */ green: "#008000",
    /**** greenYellow */ greenYellow: "#adff2f",
    /**** grey */ grey: "#808080",
    /**** honeydew */ honeydew: "#f0fff0",
    /**** hotPink */ hotPink: "#ff69b4",
    /**** indianRed */ indianRed: "#cd5c5c",
    /**** indigo */ indigo: "#4b0082",
    /**** ivory */ ivory: "#fffff0",
    /**** khaki */ khaki: "#f0e68c",
    /**** lavender */ lavender: "#e6e6fa",
    /**** lavenderBlush */ lavenderBlush: "#fff0f5",
    /**** lawnGreen */ lawnGreen: "#7cfc00",
    /**** lemonChiffon */ lemonChiffon: "#fffacd",
    /**** lightBlue */ lightBlue: "#add8e6",
    /**** lightCoral */ lightCoral: "#f08080",
    /**** lightCyan */ lightCyan: "#e0ffff",
    /**** lightGoldenrodYellow */ lightGoldenrodYellow: "#fafad2",
    /**** lightGrey */ lightGrey: "#d3d3d3",
    /**** lightGray */ lightGray: "#d3d3d3",
    /**** lightGreen */ lightGreen: "#90ee90",
    /**** lightGrey */ lightGrey: "#d3d3d3",
    /**** lightPink */ lightPink: "#ffb6c1",
    /**** lightSalmon */ lightSalmon: "#ffa07a",
    /**** lightSeagreen */ lightSeagreen: "#20b2aa",
    /**** lightSkyblue */ lightSkyblue: "#87cefa",
    /**** lightSlategray */ lightSlategray: "#778899",
    /**** lightSlategrey */ lightSlategrey: "#778899",
    /**** lightSteelblue */ lightSteelblue: "#b0c4de",
    /**** lightYellow */ lightYellow: "#ffffe0",
    /**** lime */ lime: "#00ff00",
    /**** limeGreen */ limeGreen: "#32cd32",
    /**** linen */ linen: "#faf0e6",
    /**** magenta */ magenta: "#ff00ff",
    /**** maroon */ maroon: "#800000",
    /**** mediumAquamarine */ mediumAquamarine: "#66cdaa",
    /**** mediumBlue */ mediumBlue: "#0000cd",
    /**** mediumOrchid */ mediumOrchid: "#ba55d3",
    /**** mediumPurple */ mediumPurple: "#9370db",
    /**** mediumSeaGreen */ mediumSeaGreen: "#3cb371",
    /**** mediumSlateBlue */ mediumSlateBlue: "#7b68ee",
    /**** mediumSpringGreen */ mediumSpringGreen: "#00fa9a",
    /**** mediumTurquoise */ mediumTurquoise: "#48d1cc",
    /**** mediumVioletRed */ mediumVioletRed: "#c71585",
    /**** midnightBlue */ midnightBlue: "#191970",
    /**** mintCream */ mintCream: "#f5fffa",
    /**** mistyRose */ mistyRose: "#ffe4e1",
    /**** moccasin */ moccasin: "#ffe4b5",
    /**** navajoWhite */ navajoWhite: "#ffdead",
    /**** navy */ navy: "#000080",
    /**** oldLace */ oldLace: "#fdf5e6",
    /**** olive */ olive: "#808000",
    /**** oliveDrab */ oliveDrab: "#6b8e23",
    /**** orange */ orange: "#ffa500",
    /**** orangeRed */ orangeRed: "#ff4500",
    /**** orchid */ orchid: "#da70d6",
    /**** paleGoldenrod */ paleGoldenrod: "#eee8aa",
    /**** paleGreen */ paleGreen: "#98fb98",
    /**** paleTurquoise */ paleTurquoise: "#afeeee",
    /**** paleVioletRed */ paleVioletRed: "#db7093",
    /**** papayaWhip */ papayaWhip: "#ffefd5",
    /**** peachPuff */ peachPuff: "#ffdab9",
    /**** peru */ peru: "#cd853f",
    /**** pink */ pink: "#ffc0cb",
    /**** plum */ plum: "#dda0dd",
    /**** powderBlue */ powderBlue: "#b0e0e6",
    /**** purple */ purple: "#800080",
    /**** red */ red: "#ff0000",
    /**** rosyBrown */ rosyBrown: "#bc8f8f",
    /**** royalBlue */ royalBlue: "#4169e1",
    /**** saddleBrown */ saddleBrown: "#8b4513",
    /**** salmon */ salmon: "#fa8072",
    /**** sandyBrown */ sandyBrown: "#f4a460",
    /**** seaGreen */ seaGreen: "#2e8b57",
    /**** seaShell */ seaShell: "#fff5ee",
    /**** sienna */ sienna: "#a0522d",
    /**** silver */ silver: "#c0c0c0",
    /**** skyBlue */ skyBlue: "#87ceeb",
    /**** slateBlue */ slateBlue: "#6a5acd",
    /**** slateGray */ slateGray: "#708090",
    /**** slateGrey */ slateGrey: "#708090",
    /**** snow */ snow: "#fffafa",
    /**** springGreen */ springGreen: "#00ff7f",
    /**** steelBlue */ steelBlue: "#4682b4",
    /**** tan */ tan: "#d2b48c",
    /**** teal */ teal: "#008080",
    /**** thistle */ thistle: "#d8bfd8",
    /**** tomato */ tomato: "#ff6347",
    /**** turquoise */ turquoise: "#40e0d0",
    /**** violet */ violet: "#ee82ee",
    /**** wheat */ wheat: "#f5deb3",
    /**** white */ white: "#ffffff",
    /**** whiteSmoke */ whiteSmoke: "#f5f5f5",
    /**** yellow */ yellow: "#ffff00",
    /**** yellowGreen */ yellowGreen: "#9acd32"
};

/**
    
    References
    ==========

    http://svn.mootools.net/trunk/Plugins/Color.js
      Mootools library provided algorithms for converting between HSL and RGB.
       - Michael Jackson, <http://ajaxon.com/michael>
       - Valerio Proietti, <http://mad4milk.net>
       - Christophe Beyls, <http://www.digitalia.be>
       - MIT License

    http://solutoire.com/2007/02/02/efficient-looping-in-javascript/
      A hint on improving loop performance by 25%.

    http://svn.mochikit.com/mochikit/trunk/MochiKit/Color.js
      Table of RGB hexadecimal strings for color names.

    http://www.w3.org/TR/css3-color/#X11COLORS
      Full table of CSS3 X11 colors.
      Robert B. Hess. Colors By Name.
      MSDN Online Web Workshop. 02 November 1996.

    http://juicystudio.com/services/colourcontrast.php#brightformula
      W3C recommended brightness forumla.

    http://juicystudio.com/services/colourcontrast.php#brightformula
      W3C recommended difference formula.

*/


/*license

    Legal
    =======
    
    Chiron is a component of the Tale web-game project.
    
    See <credit.txt> for a complete list of
    contributions and their licenses.  All contributions are provided
    under permissive, non-viral licenses including MIT, BSD, Creative Commons
    Attribution 2.5, Public Domain, or Unrestricted.
    
    
    License
    =======
    
    Copyright (c) 2002-2008 Kris Kowal <http://cixar.com/~kris.kowal>
    MIT License
    
    
    MIT License
    -----------
    
    Permission is hereby granted, free of charge, to any person
    obtaining a copy of this software and associated documentation
    files (the "Software"), to deal in the Software without
    restriction, including without limitation the rights to use,
    copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following
    conditions:
    
    The above copyright notice and this permission notice shall be
    included in all copies or substantial portions of the Software.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    OTHER DEALINGS IN THE SOFTWARE.

*/

